package com.plugin.core.util;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAUtil {

    private static Logger logger = LoggerFactory.getLogger(RSAUtil.class);

    public static final String KEY_ALGORITHM = "RSA";

    public static final String RSA_PADDING_KEY = "RSA/ECB/PKCS1Padding";

    public static final String SIGNATURE_ALGORITHM_MD5 = "MD5withRSA";

    public static final String SIGNATURE_ALGORITHM_SHA1 = "SHA1withRSA";

    private static final Object LOCK = new Object();

    private static final String UTF_8=String.valueOf(StandardCharsets.UTF_8);
    private static Class bcProvider = null;

    /**
     * 获取RSA公钥
     * @param key 公钥字符串
     */
    private static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes = MyBase64Util.decode(key.getBytes(UTF_8));
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }


    /**
     * 获取RSA私钥
     * @param key 密钥字符串（经过base64编码）
     */
    private static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes = MyBase64Util.decode(key.getBytes(UTF_8));
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }


    /**
     * RSA公钥加密
     * @param plainText 待加密数据
     * @param s_publicKey 公钥字符串
     */
    public static String encrypt(String plainText, String s_publicKey) {
        if (plainText == null || s_publicKey == null) {
            return null;
        }
        try {
            PublicKey publicKey = getPublicKey(s_publicKey);
            Cipher cipher = getCipherInstance();
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] enBytes = cipher.doFinal(plainText.getBytes(UTF_8));
            return formatString(new String(MyBase64Util.encode(enBytes), UTF_8));
        } catch (Exception e) {
            logger.error("encrypt failed the error is ",e);
        }
        return null;
    }


    /**
     * RSA私钥解密
     * @param enStr 待解密数据
     * @param s_privateKey 私钥字符串
     */
    public static String decrypt(String enStr, String s_privateKey) {
        if (enStr == null || s_privateKey == null) {
            return null;
        }
        try {
            PrivateKey privateKey = getPrivateKey(s_privateKey);
            Cipher cipher = getCipherInstance();
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] deBytes = cipher.doFinal(MyBase64Util.decode(enStr.getBytes(UTF_8)));
            return new String(deBytes, UTF_8);
        } catch (Exception e) {
            logger.error("exception",e);
        }
        return null;
    }


    /**
     * 格式化RSA加密字符串,去掉换行和渐近符号
     */
    private static String formatString(String sourceStr) {
        if (sourceStr == null) {
            return null;
        }
        return sourceStr.replaceAll("\\r", "").replaceAll("\\n", "");
    }




    private static Cipher getCipherInstance() throws NoSuchAlgorithmException, NoSuchPaddingException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        String jdkVer = System.getProperty("java.version");
        if(jdkVer.startsWith("1.4.")) {
            if (bcProvider == null) {
                synchronized (LOCK) {
                    if (bcProvider == null) {
                        bcProvider = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
                    }
                }
            }
            Provider provider = (Provider) bcProvider.newInstance();
            return Cipher.getInstance(RSA_PADDING_KEY, provider);
        } else {
            return Cipher.getInstance(RSA_PADDING_KEY);
        }
    }


    /**
     * RSA签名
     * MD5摘要RSA签名
     * @param content 待签名数据
     * @param privateKey 关联方私钥
     */
    public static String sign(String content, String privateKey,String signatureAlgorithm) {
        if (content == null || privateKey == null || signatureAlgorithm == null) {
            return null;
        }
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(MyBase64Util.decode(privateKey.getBytes(UTF_8)));
            KeyFactory keyf = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            Signature signature = null;
            if(SIGNATURE_ALGORITHM_MD5.equals(signatureAlgorithm)){
                signature = Signature.getInstance(SIGNATURE_ALGORITHM_MD5);
            }else if(SIGNATURE_ALGORITHM_SHA1.equals(signatureAlgorithm)){
                signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA1);
            }
            signature.initSign(priKey);
            signature.update(content.getBytes(UTF_8));
            byte[] signed = signature.sign();
            return new String(MyBase64Util.encode(signed), UTF_8);
        } catch (Exception e) {
            logger.error("exception",e);
        }
        return null;
    }

    /**
     * RSA签名验证
     * MD5摘要RSA签名验证
     * @param content 待签名数据
     * @param sign 签名值
     * @param publicKey 分配给关联方公钥
     * @return 布尔值
     */
    public static boolean verifySign(String content, String sign, String publicKey,String signatureAlgorithm) {
        if (content == null || sign == null || publicKey == null || signatureAlgorithm == null) {
            return false;
        }
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            byte[] encodedKey = MyBase64Util.decode(publicKey.getBytes(UTF_8));
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            Signature signature = null;
            if(SIGNATURE_ALGORITHM_MD5.equals(signatureAlgorithm)){
                signature = Signature.getInstance(SIGNATURE_ALGORITHM_MD5);
            }else if(SIGNATURE_ALGORITHM_SHA1.equals(signatureAlgorithm)){
                signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA1);
            }

            signature.initVerify(pubKey);
            signature.update(content.getBytes(UTF_8));
            return signature.verify(MyBase64Util.decode(sign.getBytes(UTF_8)));
        } catch (Exception e) {
            logger.error("exception:",e);
        }
        return false;
    }


    /**
     * RSA：随机生成密钥对
     *
     */
    public static void genKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);

        // 初始化密钥对生成器，密钥大小为96-2048位
        keyPairGen.initialize(2048, new SecureRandom());

        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   	// 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  		// 得到公钥

        // 得到公钥字符串
        String publicKeyStr = new String(MyBase64Util.encode(publicKey.getEncoded()));
        // 得到私钥字符串
        String privateKeyStr = new String(MyBase64Util.encode((privateKey.getEncoded())));

        System.out.println("随机生成的公钥为:" + publicKeyStr);
        System.out.println("随机生成的私钥为:" + privateKeyStr);
    }
}
